package com.abel.bigwater.data.kente

import com.abel.bigwater.data.BcdString
import com.abel.bigwater.data.NotEnoughDataException
import com.abel.bigwater.data.UnknownPacketException
import com.abel.bigwater.model.JsonDateDeserializer
import com.abel.bigwater.model.JsonDateSerializer
import com.abel.bigwater.model.JsonHelper
import com.alibaba.fastjson.annotation.JSONField
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
import com.fasterxml.jackson.databind.annotation.JsonSerialize
import io.netty.buffer.ByteBuf
import org.apache.commons.codec.digest.DigestUtils
import org.joda.time.DateTime
import org.slf4j.LoggerFactory
import java.text.SimpleDateFormat
import java.util.*
import kotlin.experimental.and

/**
 * 数据整包：
如： 41 54 2B 44 61 74 61 3D 0F A1 9B 98 00 49 C6 33 01 64 13 01 00 02 AB
5C 42 11 7B B5 3E 56 FF 5D 3E E5 58 02 00 00 00 00 00 1F 02 00 00 05 05
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
03 75
 * 总字节 共（64 字节）
 *
 * @author Abel
 */
open class KentePacket {
    companion object {
        const val head = "AT+Data="

        const val dataLen = 64

        private val lgr = LoggerFactory.getLogger(KentePacket::class.java)

        fun parse(buf: ByteBuf): List<KentePacket>? {
            if (buf.readableBytes() < head.length) return null

            val head2 = ByteArray(head.length).apply { buf.duplicate().readBytes(this) }
            if (head2.contentEquals(head.toByteArray())) {
                // continue to parse
            } else if (KenteDatagram.head.toByteArray().contentEquals(ByteArray(KenteDatagram.head.length).apply { buf.duplicate().readBytes(this) })) {
                return listOf(KenteDatagram(String(ByteArray(buf.readableBytes()).apply { buf.readBytes(this) })))
            } else {
                throw UnknownPacketException("不支持的消息: ${head2.toString(Charsets.UTF_8)}")
            }

            if (buf.readableBytes() < head.length + 1 + dataLen) {
                throw NotEnoughDataException("字节数不足一帧: ${buf.readableBytes()}")
            }

            val cnt = (buf.readableBytes() - head.length - 1) / dataLen
            val dlist = ArrayList<KentePacket>()

            val buf1 = buf.readBytes(head.length + 1 + cnt * dataLen)
            buf1.skipBytes(head.length)
            val rssi0 = buf1.readByte().toShort().and(0xFF)

            for (idx in 1..cnt) {
                buf1.readBytes(dataLen).also {
                    dlist.add(KentePacket().apply {
                        command = head
                        backArray = ByteArray(it.readableBytes()).apply { it.duplicate().readBytes(this) }
                        rssi = rssi0

                        rtuId = it.readIntLE().toLong().and(0xFFFFFFFF)

                        dateInt = it.readIntLE().toLong().and(0xFFFFFFFF)
                        timeInt = it.readIntLE().toLong().and(0xFFFFFFFF)

                        flow = it.readFloatLE()

                        pressure = it.readFloatLE()

                        speed = it.readFloatLE()

                        forwardDigits = it.readIntLE().toLong().and(0xFFFFFFFF)
                        revertDigits = it.readIntLE().toLong().and(0xFFFFFFFF)
                        forwardPoint = it.readShortLE().toInt().and(0xFFFF)
                        revertPoint = it.readShortLE().toInt().and(0xFFFF)

                        meterVolt = it.readByte().toShort().and(0xFF)
                        rtuVolt = it.readByte().toShort().and(0xFF)

                        commError = it.readByte()
                        emptyError = it.readByte()
                        magnetError = it.readByte()
                        flowMaxError = it.readByte()
                        flowMinError = it.readByte()
                        pressureMaxError = it.readByte()
                        pressureMinError = it.readByte()

                        it.skipBytes(reservedBytes.size)
                        crc16 = it.readShortLE()
                    })
                }
            }

            return dlist
        }
    }

    var command: String? = null

    var backArray: ByteArray? = null

    var msg: String? = null
        get() = if (field == null) BcdString.hexString(backArray!!) else field

    var len: Int? = null
        get() = if (backArray == null) msg?.length else backArray?.size

    /**
     * 信号 1 字节 0F 十六进制转十 15
     */
    var rssi: Short? = null

    /**
     * GPRS 模块识别码 4 字节 A19B9800（00 98 9B A1） 十六进制转十 10001313
     */
    var rtuId: Long? = null

    /**
     * 数据读取日期 4 字节 49C63301(01 33 C6 49) 十六进制转十 20170313
     */
    var dateInt: Long? = null

    /**
     * 数据读取时间 4 字节 64130100(00 01 13 64) 十六进制转十 7:05:00
     */
    var timeInt: Long? = null

    @JsonSerialize(using = JsonDateSerializer::class)
    @JsonDeserialize(using = JsonDateDeserializer::class)
    @JSONField(format = JsonHelper.FULL_DATE_FORMAT)
    var sampleTime: Date? = null
        get() {
            return if (field != null) field
            else if (dateInt == null || timeInt == null) null
            else SimpleDateFormat("yyyyMMdd HHmmss").parse("${dateInt} ${("000000" + timeInt).takeLast(6)}")
        }

    /**
     * 瞬时流量， m3/h 4 字节 02AB5C42(42 5C AB 02) Float(IEEE754
    32-bit) 55.167
     */
    var flow: Float? = null

    /**
     * 瞬时压力， Mpa 4 字节 117BB53E(3E B5 7B 11) Float(IEEE754
    32-bit) 0.354455
     */
    var pressure: Float? = null

    /**
     * 瞬时流速， m3/h 4 字节 56FF5D3E(3E 5D FF 56) Float(IEEE754
    32-bit) 0.216794
     */
    var speed: Float? = null

    /**
     * 正向累计整数 4 字节 E5580200(00 02 58 E5) 十六进制转十 153829
     */
    var forwardDigits: Long? = null

    /**
     * 反向累计整数 4 字节 00 00 00 00 十六进制转十 (同上)0
     */
    var revertDigits: Long? = null

    /**
     * 正向累计的小数
    /1000
    2 字节 1F02(02 1F) 十六进制转十 543（.543）
     */
    var forwardPoint: Int? = null

    /**
     * 反向累计的小数
    /1000
    2 字节 00 00 十六进制转十 (同上)0
     */
    var revertPoint: Int? = null

    var forwardReading: Double? = null
        get() {
            return if (field != null) field else (forwardDigits ?: 0) + ((forwardPoint ?: 0) / 1000.0)
        }

    var revertReading: Double? = null
        get() = if (field != null) field else (revertDigits ?: 0) + ((revertPoint ?: 0) / 1000.0)

    /**
     * 水表电池电量 1 字节 05 5
     */
    var meterVolt: Short? = null

    /**
     * GPRS 电池电量 1 字节 05 5
     */
    var rtuVolt: Short? = null

    /**
     * 系统报警，即表通
    讯失败 1 字节 00 00 或 01
     */
    var commError: Byte? = null

    /**
     * 水表空管报警 1 字节 00 00 或 01
     */
    var emptyError: Byte? = null

    /**
     * 断励磁报警 1 字节 00 00 或 01
     */
    var magnetError: Byte? = null

    /**
     * 流量上线报警 1 字节 00 00 或 01
     */
    var flowMaxError: Byte? = null

    /**
     * 流量下线报警 1 字节 00 00 或 01
     */
    var flowMinError: Byte? = null

    /**
     * 压力上限报警 1 字节 00 00 或 01
     */
    var pressureMaxError: Byte? = null

    /**
     * 压力下限报警 1 字节 00 00 或 01
     */
    var pressureMinError: Byte? = null

    /**
     * 预留 17 字节 17 字节 .............. 00
     */
    val reservedBytes = ByteArray(17) { idx -> 0 }

    /**
     * 参数保存 CRC 2 字节 03 75 CRC16-循环冗余校
    验（去包头、信号）
     */
    var crc16: Short? = null

    override fun toString(): String {
        return "$rtuId: $command,usage=${forwardReading}-${revertReading} @${sampleTime}."
    }
}